import asyncio
import subprocess
import time
import re
import select
import socket
import struct
import traceback
import binascii

from ctypes import *

import serial
from select import epoll
from typing import Optional
from multiprocessing import sharedctypes as mps
from multiprocessing import Process, Queue, Event
from CarApi.TestToolApi import Debug
from CarApi.NewBaseData import Gps, MainBoard, Control


class TranceProcess(Process):

    def __init__(self, recvAddr: tuple, logging=True, daemon=True):
        super(TranceProcess, self).__init__(daemon=daemon)
        if logging:
            self.__mainDebugQueue = Queue(10)
        else:
            self.__mainDebugQueue = None
        self.__mainDebug = Debug(self.__mainDebugQueue, True, '下位机-服务端模块-通信进程')
        self.__recvAddr = recvAddr
        self.__commandQueue = Queue(20)
        self.__CloseStatus = Event()
        self.__ClientCloseStatus = Event()
        self.__TrancePauseStatus = Event()
        self.__RecvStatus = Event()
        self.__RecvStatus.clear()
        self.__serial = serial.Serial('/dev/ttyAMA0', 115200, timeout=0)
        self.MAIN_BOARD_DATA: Optional[mps.Value] = None
        self.GPS_DATA: Optional[mps.Value] = None
        self.SERIAL_DATA: Optional[mps.Value] = None
        stdout, _ = subprocess.Popen(
            "ls -l /sys/class/video4linux/video* | grep scb",
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            shell=True
        ).communicate()
        dataList = stdout.decode().split('\n')
        firstVideo = dataList[0][-6:]
        secondVideo = dataList[1][-6:]
        stdout, _ = subprocess.Popen(
            f"v4l2-ctl --list-formats -d /dev/{firstVideo} | grep MJPG",
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            shell=True
        ).communicate()
        self.__cameraSub = secondVideo if stdout == b'' else firstVideo
        self.__mainDebug.debug(f"副摄像头为->{self.__cameraSub}")
        stdout, _ = subprocess.Popen(
            "ls -l /sys/class/video4linux/video* | grep virtual",
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            shell=True
        ).communicate()
        self.__cameraMain = stdout.decode().split(' ')[-3][-6:]
        self.__mainDebug.debug(f"主摄像头为->{self.__cameraMain}")
        stdout, _ = subprocess.Popen(
            "arecord -l | grep card",
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            shell=True
        ).communicate()
        audioNum1 = stdout.decode().split(' ')[1].replace(':', '')
        audioNum2 = stdout.decode().split(' ')[7].replace(':', '')
        self.__audioDevice = f'plughw:{audioNum1},{audioNum2}'
        self.__mainDebug.debug(f"音频设备为->{self.__audioDevice}")

    async def __messageRecv(self, client: socket.socket, saveBuffer: asyncio.Queue):
        self.__mainDebug.info("[消息接收器] 已进入该任务")
        lastTime = time.time()
        controlPack = struct.Struct("< B ? ? ? ? f")
        while not self.__TrancePauseStatus.is_set():
            try:
                rlist, _, _ = select.select([client], [], [], 0)
                if len(rlist) == 0:
                    if time.time() - lastTime < 400:
                        await asyncio.sleep(0)
                    elif time.time() - lastTime < 800:
                        self.__mainDebug.warn("[消息接收器] 客户端延时大于400ms, 禁止运动!")
                        await saveBuffer.put(controlPack.pack(1, False, False, False, False, 0))
                    elif time.time() - lastTime < 1200:
                        self.__mainDebug.error("[消息接收器] 客户端延时大于1200ms, 主动断开客户端连接!")
                        self.__TrancePauseStatus.set()
                        break
                else:
                    lastTime = time.time()
                    data = client.recv(1024)
                    if data == b'':
                        await asyncio.sleep(0)
                    else:
                        # self.__mainDebug.debug(f"[消息接收器] 已接收到数据: {data}")
                        await saveBuffer.put(data)
            except Exception as e:
                self.__mainDebug.error(
                    f"\n{traceback.format_exc()}\n----------Error----------"
                )
                self.__mainDebug.error("[消息接收器] 该任务发生错误, 退出!")
                break
        if self.__TrancePauseStatus.is_set() and not self.__ClientCloseStatus.is_set():
            self.__mainDebug.warn("[消息接收器] 出现全局错误, 退出该任务!")
        elif self.__TrancePauseStatus.is_set() and self.__ClientCloseStatus.is_set():
            self.__mainDebug.debug("[消息接收器] 客户端断开连接，自动结束服务")
        self.__TrancePauseStatus.set()
        self.__mainDebug.warn("[消息接收器] 任务被中止!")

    async def __messageFresh(self, client: socket.socket, saveBuffer: asyncio.Queue):
        self.__mainDebug.info("[消息刷新器] 已进入该任务")
        controlPack = struct.Struct("< B ? ? ? ? f")
        heartBeatPack = struct.Struct("< B d ?")
        sentHeartBeatPack = struct.Struct("< B d f f")
        proc = None
        while not self.__TrancePauseStatus.is_set():
            try:
                if not saveBuffer.empty():
                    rawData = await saveBuffer.get()
                    # print(len(rawData))
                    if rawData[0] == 1 and len(rawData) == 9:
                        data = controlPack.unpack(rawData)
                        # print(data)
                        if data[3] and not data[4]:
                            # 走转
                            self.__mainDebug.debug("[消息刷新器] 左转")
                            self.SERIAL_DATA.status = 3
                        elif data[4] and not data[3]:
                            # 右转
                            self.__mainDebug.debug("[消息刷新器] 右转")
                            self.SERIAL_DATA.status = 4
                        elif data[1] and not data[2]:
                            # 前
                            self.__mainDebug.debug("[消息刷新器] 前进")
                            self.SERIAL_DATA.status = 1
                        elif data[2] and not data[1]:
                            # 后退
                            self.__mainDebug.debug("[消息刷新器] 后退")
                            self.SERIAL_DATA.status = 2
                        elif data[1] == data[2] == data[3] == data[4]:
                            # 禁止操作
                            self.__mainDebug.debug("[消息刷新器] 停止")
                            self.SERIAL_DATA.status = 0
                        if data[5]:
                            # 速度
                            self.__mainDebug.debug(f"[消息刷新器] 速度->{data[5]}")
                            self.SERIAL_DATA.speed_qt = data[5]
                    elif rawData[0] == 2 and len(rawData) == 10:
                        data = heartBeatPack.unpack(rawData)
                        timeTemp = data[1]
                        tranceStatus = data[2]
                        if tranceStatus and not self.__RecvStatus.is_set():
                            proc = subprocess.Popen(
                                "ffplay -i rtmp://139.155.181.21/live/03 -fflags nobuffer -analyzeduration 10000",
                                stdout=subprocess.DEVNULL,
                                stderr=subprocess.DEVNULL,
                                shell=True
                            )
                            self.__RecvStatus.set()
                        elif not tranceStatus and self.__RecvStatus.is_set():
                            proc.terminate()
                            self.__RecvStatus.clear()
                        netUp, netDown = self.MAIN_BOARD_DATA.net.up, self.MAIN_BOARD_DATA.net.down
                        client.send(sentHeartBeatPack.pack(2, timeTemp, netUp, netDown))
                        await asyncio.sleep(0)
                    else:
                        await asyncio.sleep(0)
                else:
                    await asyncio.sleep(0)
            except Exception as e:
                self.__mainDebug.error(
                    f"\n{traceback.format_exc()}\n----------Error----------"
                )
                self.__mainDebug.error("[消息刷新器] 该任务发生错误, 退出!")
                break
        if self.__TrancePauseStatus.is_set() and not self.__ClientCloseStatus.is_set():
            self.__mainDebug.warn("[消息刷新器] 出现全局错误, 退出该任务!")
        elif self.__TrancePauseStatus.is_set() and self.__ClientCloseStatus.is_set():
            self.__mainDebug.debug("[消息刷新器] 客户端断开连接，自动结束服务")
        self.__TrancePauseStatus.set()
        if proc is not None:
            proc.terminate()
        self.__mainDebug.warn("[消息刷新器] 任务被中止!")

    async def __messageGet(self, saveBuffer: asyncio.Queue):
        self.__mainDebug.info("[消息获取器] 已进入该任务")
        statusPack = struct.Struct("< B f f f B")
        sendPack = struct.Struct("< f f f")
        mapSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        while not self.__TrancePauseStatus.is_set():
            try:
                await asyncio.sleep(1)
                gpsSpeed = self.GPS_DATA.gps.gdRate
                gpsLit = self.GPS_DATA.gps.lit
                gpsDim = self.GPS_DATA.gps.dim

                signal = self.GPS_DATA.net.signal
                status = self.GPS_DATA.net.status
                stm32Speed = self.SERIAL_DATA.speed_stm32
                stm32Bat = self.SERIAL_DATA.bat
                # stm32Speed = 5.76
                # stm32Bat = 83.5

                stm32Yaw = self.SERIAL_DATA.yaw
                mapSocket.sendto(sendPack.pack(gpsLit, gpsDim, stm32Yaw), ('127.0.0.1', 9999))
                if not status:
                    signal = 0
                # print(stm32Speed)
                await saveBuffer.put(statusPack.pack(1, stm32Bat, stm32Speed, gpsSpeed, signal))
            except Exception as e:
                self.__mainDebug.error(
                    f"\n{traceback.format_exc()}\n----------Error----------"
                )
                self.__mainDebug.error("[消息获取器] 该任务发生错误, 退出!")
                break
        if self.__TrancePauseStatus.is_set() and not self.__ClientCloseStatus.is_set():
            self.__mainDebug.warn("[消息获取器] 出现全局错误, 退出该任务!")
        elif self.__TrancePauseStatus.is_set() and self.__ClientCloseStatus.is_set():
            self.__mainDebug.debug("[消息获取器] 客户端断开连接，自动结束服务")
        self.__TrancePauseStatus.set()
        self.__mainDebug.warn("[消息获取器] 任务被中止!")

    async def __messageSent(self, client: socket.socket, saveBuffer: asyncio.Queue):
        self.__mainDebug.info("[消息发送器] 已进入该任务")
        while not self.__TrancePauseStatus.is_set():
            try:
                if not saveBuffer.empty():
                    rawData = await saveBuffer.get()
                    client.send(rawData)
                    await asyncio.sleep(0)
                else:
                    await asyncio.sleep(0)
            except BrokenPipeError as e:
                self.__mainDebug.warn("[消息发送器] 客户端因为未知原因断开连接, 退出!")
                break
            except Exception as e:
                self.__mainDebug.error(
                    f"\n{traceback.format_exc()}\n----------Error----------"
                )
                self.__mainDebug.error("[消息发送器] 该任务发生错误, 退出!")
                break
        if self.__TrancePauseStatus.is_set() and not self.__ClientCloseStatus.is_set():
            self.__mainDebug.warn("[消息发送器] 出现全局错误, 退出该任务!")
        elif self.__TrancePauseStatus.is_set() and self.__ClientCloseStatus.is_set():
            self.__mainDebug.debug("[消息发送器] 客户端断开连接，自动结束服务")
        self.__TrancePauseStatus.set()
        self.__mainDebug.warn("[消息发送器] 任务被中止!")

    def run(self) -> None:
        self.__mainDebug.info("进程启动中...")
        recvAddr = self.__recvAddr
        while not self.__CloseStatus.is_set():
            try:
                self.__mainDebug.info("等待连接服务器")
                while self.__TrancePauseStatus.is_set():
                    if self.__CloseStatus.is_set():
                        break
                    time.sleep(0.1)
                if self.__CloseStatus.is_set():
                    break
                else:
                    self.__mainDebug.info("准备监听客户端中...")
                    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    server.bind(recvAddr)
                    server.listen(1)
                    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    self.__mainDebug.info("监听客户端中...")
                    new_server, _ = server.accept()
                    new_server.send(b'Hello')
                    self.__ClientCloseStatus.clear()
                    self.__mainDebug.info("客户端连接成功")
                    loop = asyncio.new_event_loop()

                    recvQueue = asyncio.Queue(20, loop=loop)
                    sentQueue = asyncio.Queue(20, loop=loop)
                    proc1 = subprocess.Popen(
                        f"ffmpeg -f video4linux2 -r 30 -s 640x480 -i /dev/{self.__cameraMain} -f alsa -i '{self.__audioDevice}' -ac 2 -ar 48000 -b:v 1000k -b:a 500k -vprofile baseline -vcodec h264_omx -f flv rtmp://139.155.181.21/live/01",
                        stderr=subprocess.DEVNULL,
                        stdout=subprocess.DEVNULL,
                        shell=True
                    )
                    proc2 = subprocess.Popen(
                        f"ffmpeg -f video4linux2 -s 640x480 -i /dev/{self.__cameraSub} -b:v 1000k -vprofile baseline -vcodec h264_omx -f flv rtmp://139.155.181.21/live/02",
                        stderr=subprocess.DEVNULL,
                        stdout=subprocess.DEVNULL,
                        shell=True
                    )
                    self.__mainDebug.info("主音视频流推流开启成功")

                    async def main():
                        messageRecv = loop.create_task(self.__messageRecv(new_server, recvQueue))
                        messageFresh = loop.create_task(self.__messageFresh(new_server, recvQueue))
                        messageGet = loop.create_task(self.__messageGet(sentQueue))
                        messageSent = loop.create_task(self.__messageSent(new_server, sentQueue))
                        await asyncio.gather(messageRecv, messageFresh, messageGet, messageSent)

                    loop.run_until_complete(main())
                    server.close()
                    proc1.terminate()
                    proc2.terminate()
                    self.__mainDebug.info("全任务结束, 重新进入等待")
            except Exception as e:
                self.__mainDebug.error(
                    f"\n{traceback.format_exc()}\n----------Error----------"
                )
                self.__mainDebug.error("等待客户端连接时发生错误，退出!")
                break
        if self.__CloseStatus.is_set():
            self.__mainDebug.warn("出现全局错误申请, 退出该进程!")
        self.__CloseStatus.set()
        self.__mainDebug.debug("进程中止!")

    def play(self):
        self.__TrancePauseStatus.clear()

    def pause(self):
        self.__TrancePauseStatus.set()

    def stop(self):
        self.__CloseStatus.set()

    def isConnected(self):
        return not self.__TrancePauseStatus.is_set()


if __name__ == '__main__':
    tranceProcess = TranceProcess(('0.0.0.0', 8500))
    mainBoardProcess = MainBoard()
    gpsBoardProcess = Gps()
    controlProcess = Control()

    tranceProcess.MAIN_BOARD_DATA = mainBoardProcess.MAIN_BOARD_DATA
    tranceProcess.GPS_DATA = gpsBoardProcess.GPS_DATA
    tranceProcess.SERIAL_DATA = controlProcess.SERIAL_DATA

    mainBoardProcess.start()
    gpsBoardProcess.start()
    controlProcess.start()
    tranceProcess.start()
    while True:
        try:
            if tranceProcess.isConnected():
                # print("开启")
                mainBoardProcess.play()
                gpsBoardProcess.play()
                controlProcess.play()
                time.sleep(1)
            else:
                mainBoardProcess.pause()
                gpsBoardProcess.pause()
                controlProcess.pause()
                time.sleep(3)
                tranceProcess.play()
        except Exception as e:
            print(repr(e))
            break
    tranceProcess.pause()
    mainBoardProcess.pause()
    gpsBoardProcess.pause()
    controlProcess.pause()
    tranceProcess.stop()
    mainBoardProcess.stop()
    gpsBoardProcess.stop()
    controlProcess.stop()
    tranceProcess.join()
    mainBoardProcess.join()
    gpsBoardProcess.join()
    controlProcess.join()
